// START WRAPPER: The YUI.add wrapper is added by the build system, when you use YUI Builder to build your component from the raw source in this file
 YUI.add("frontpage-slideshow", function(Y) {
	 
	 if(!window.fireImgFetchGlobalEvent){
		 window.fireImgFetchGlobalEvent = function (e, param) {//'imgPreFetched'
			  YUI().use('event-custom', function (Y) {
			    var publisher = new Y.EventTarget();
			    publisher.publish(e, {
			      broadcast:  2,   // global notification
			      emitFacade: true // emit a facade so we get the event target
			    }).fire(param);
			  });
		};
	 }
	 
    /* Any frequently used shortcuts, strings and constants */
    var Lang = Y.Lang,
    	Array = Y.Array,
    	al = Y.WidgetPositionAlign;
    
//    FrontpageSlideShow.JSONP_URL = "http://www.m3958.com";
//    
//	  if(window.location.href.indexOf('8888') != -1){
//		  FrontpageSlideShow.JSONP_URL = "http://127.0.0.1:8888";
//	  }
	  
//	  FrontpageSlideShow.JSONP_URL = "";
	  
	  //_subOperationType=NAMED_QUERY,
	  

    /* FrontpageSlideShow class constructor */
    function FrontpageSlideShow(config) {
        FrontpageSlideShow.superclass.constructor.apply(this, arguments);
    }

    /* 
     * Required NAME static field, to identify the Widget class and 
     * used as an event prefix, to generate class names etc. (set to the 
     * class name in camel case). 
     */
    FrontpageSlideShow.NAME = "FrontpageSlideShow";
    
    FrontpageSlideShow.IMG_TEMPLATE = '<img alt="" src="{url}" class="fp-slide-img">';

    /*
     * The attribute configuration for the widget. This defines the core user facing state of the widget
     */
    FrontpageSlideShow.ATTRS = {
    	jsonpUrl : {
    		value : ''
    	},
    	titleLength : {
    		value : 10
    	},
    	titleHeight :{
    		value : 30
    	},
    	titlePosition : {
    		value : 'bottom'
    	},
    	numBoxAlign : {
    		value : 'horizontal'
    	},
    	imgSize : {
    		value : '320x240'
    	},
    	interval : {
    		value : 2
    	},
    	siteId : {
    		value : null
    	},
    	tags : {
    		value : null
    	},
    	slideControl : {
    		value : null
    	},
    	preFetchNum : {
    		value : 2
    	},
    	imgBaseUrl : {
    		value : ''
    	},
    	inComingIndex :{
    		value : -1
    	},
    	outGoingIndex : {
    		value : -1
    	},
    	cachedGroups :{
    		value : {}
    	},
    	imgGroup :{
    		value : {name:null,imgNodes:[]},
    		setter : function(ig){
    			var name = ig.name,imgs = ig.imgNodes;
    			
				var imgNodes =  Y.Array.map(imgs,function(item){
					if(Lang.isString(item)){
						var imgNode = Y.Node.create(Lang.sub(FrontpageSlideShow.IMG_TEMPLATE,{url:item}));
						return imgNode;
					}
				},this);
				return {name:name,imgNodes:imgNodes};
    		}
    	},
    	zIndex : {
    		value : 0
    	},
    	
    	visualEffect : {
    		value :  {
	    		outgoing: {
	    	        opacity: 0,
	    	        duration: 1,
	    	        easing: 'ease-out'
	    	    },
	    	    incoming: {
	    	        opacity: 1,
	    	        duration: 1,
	    	        easing: 'ease-in'
	    	    }
    	    }
    	},
    	autoSlide : {
    		value : true
    	},
        attrA : {
            value: "A"                     // The default value for attrA, used if the user does not set a value during construction.

            /*
            , valueFn: "_defAttrAVal"      // Can be used as a substitute for "value", when you need access to "this" to set the default value.
             
            , setter: "_setAttrA"          // Used to normalize attrA's value while during set. Refers to a prototype method, to make customization easier
            , getter: "_getAttrA"          // Used to normalize attrA's value while during get. Refers to a prototype method, to make customization easier
            , validator: "_validateAttrA"  // Used to validate attrA's value before updating it. Refers to a prototype method, to make customization easier

            , readOnly: true               // Cannot be set by the end user. Can be set by the component developer at any time, using _set
            , writeOnce: true              // Can only be set once by the end user (usually during construction). Can be set by the component developer at any time, using _set
            
            , lazyAdd: false               // Add (configure) the attribute during initialization. 
            
                                           // You only need to set lazyAdd to false if your attribute is
                                           // setting some other state in your setter which needs to be set during initialization 
                                           // (not generally recommended - the setter should be used for normalization. 
                                           // You should use listeners to update alternate state). 

            , broadcast: 1                 // Whether the attribute change event should be broadcast or not.
            */
        }
        
        // ... attrB, attrC, attrD ... attribute configurations. 

        // Can also include attributes for the super class if you want to override or add configuration parameters
    };

    /* 
     * The HTML_PARSER static constant is used if the Widget supports progressive enhancement, and is
     * used to populate the configuration for the FrontpageSlideShow instance from markup already on the page.
     */
    FrontpageSlideShow.HTML_PARSER = {

        attrA: function (srcNode) {
            // If progressive enhancement is to be supported, return the value of "attrA" based on the contents of the srcNode
        }

    };

    /* Templates for any markup the widget uses. Usually includes {} tokens, which are replaced through Y.substitute */
    FrontpageSlideShow.MUBMER_BOX_TEMPLATE = '<div class="{nbclass}"></div>';
    FrontpageSlideShow.MUBMER_ITEM_TEMPLATE = '<div class="m3958-outbox {vorh}"><div class="m3958-innerbox"><a href="#{itemNum}">{itemNum}</a></div></div>';
    FrontpageSlideShow.NUMBER_BOX_CLASS = Y.ClassNameManager.getClassName(FrontpageSlideShow.NAME.toLowerCase(), "number-box");
    
    FrontpageSlideShow.IMG_BOX_CLASS = Y.ClassNameManager.getClassName(FrontpageSlideShow.NAME.toLowerCase(), "img-box");
    
    FrontpageSlideShow.TITLE_LINK_TEMPLATE = '<a href="{url}" title="{title}">{stitle}</a>';

    /* FrontpageSlideShow extends the base Widget class */
    Y.extend(FrontpageSlideShow, Y.Widget, {

        initializer: function() {
            /*
             * initializer is part of the lifecycle introduced by 
             * the Base class. It is invoked during construction,
             * and can be used to setup instance specific state or publish events which
             * require special configuration (if they don't need custom configuration, 
             * events are published lazily only if there are subscribers).
             *
             * It does not need to invoke the superclass initializer. 
             * init() will call initializer() for all classes in the hierarchy.
             */
	      	  var 	jsonpUrl = this.get('jsonpUrl'),
	      	  		myDataSource = this.myDataSource = new Y.DataSource.Get({
	      	  			source: jsonpUrl + "/smartcfud?_jsonp=true&_modelName=com.m3958.firstgwt.server.model.Article&_operationType=fetch&_sortBy=-publishedAt&_subOperationType=NAMED_QUERY&_jsonv=v&_queryName=findArticlesByTagNames"
	      	  		});
	    	  
	    	//  "status":0,"startRow":0,"endRow":5,"totalRows"
	    	  myDataSource.plug(Y.Plugin.DataSourceJSONSchema, {
	    	      schema: {
	    	      	metaFields : {status:'response.status',startRow:'response.startRow',endRow:'response.endRow',totalRows:'response.totalRows'},
	    	          resultListLocator: "response.data",
	    	          resultFields: ['title','id','content','excerpt','titleImg','contentImgs','attachments',"keyValues","publishedAt","url"]
	    	      }
	    	  });
	    	  
	    	  myDataSource.plug(Y.Plugin.DataSourceCache, { max: 100 });
        	
             var cb = this.get('contentBox'),
             	imgBox = this.imgBox = Y.Node.create(Y.Lang.sub('<div class="{ibclass}"></div>',{ibclass:FrontpageSlideShow.IMG_BOX_CLASS})),
                titleBox = this.titleBox = Y.Node.create('<div class="m3958-fp-slideshow-title-box"><span class="slide-title-span"></span></div>'),
                titleSpan = this.titleSpan = titleBox.one('.slide-title-span'),
                titleHeight = this.get('titleHeight'),
                imgSize = this.get('imgSize'),
                imgSizes = imgSize.split('x');
             	
           	imgBox.setStyles({height:imgSizes[1] + "px"});
           	titleBox.setStyles({width:"100%"});
           	cb.append(imgBox);
            cb.append(titleBox);
            
            this.set('height',parseInt(imgSizes[1]) + titleHeight);
            	 
             this.preFetchInProgress = false;
             this.waitingForPreFetch = 1;
             var nb = this.numberBoxNode = Y.Node.create(Y.Lang.sub(FrontpageSlideShow.MUBMER_BOX_TEMPLATE,{nbclass:FrontpageSlideShow.NUMBER_BOX_CLASS}));
             cb.appendChild(nb);
             var n = this.imgFetchIframe = Y.Node.create('<iframe style="visibility:hidden;display:none" mce_style="visibility: hidden; display: none;"/> ');
             Y.one('body').append(n);
        },

        destructor : function() {
            /*
             * destructor is part of the lifecycle introduced by 
             * the Widget class. It is invoked during destruction,
             * and can be used to cleanup instance specific state.
             *
             * Anything under the boundingBox will be cleaned up by the Widget base class
             * We only need to clean up nodes/events attached outside of the bounding Box
             *
             * It does not need to invoke the superclass destructor. 
             * destroy() will call initializer() for all classes in the hierarchy.
             */
        },

        renderUI : function() {
            /*
             * renderUI is part of the lifecycle introduced by the
             * Widget class. Widget's renderer method invokes:
             *
             *     renderUI()
             *     bindUI()
             *     syncUI()
             *
             * renderUI is intended to be used by the Widget subclass
             * to create or insert new elements into the DOM. 
             */
        	if(this.get('slideControl')){
        		this.get('slideControl').render();
        	}
        	this.__setImgNodes();
	    	if(this.get('autoSlide')){
	    		this._autoSlide();
	    	}
        },
        
        articleArrival : function(articles){//一个section一次性加载
        	var imgBaseUrl = this.get('imgBaseUrl'),
        		preFetchNum = this.get('preFetchNum');
        	
        	var imgNodes = Y.Array.map(articles,function(article){
        		var img = article.gett('titleImg');
        		if(img){
        			return imgBaseUrl + this._getImgUrl(img);
        		}else{
        			return null;
        		}
        	},this);
        	
        	imgNodes = Y.Array.filter(imgNodes,function(item){
        		return item;
        	});
        	var hasImgArticles = Y.Array.filter(articles,function(a){
        		return a.gett('titleImg');
        	});
        	this.articleImgNode = {articles:hasImgArticles,imgNodes:imgNodes,fetchedIndex : 0};
        	//不要马上set，稍等。
        	if(preFetchNum > 0){
        		this.set('imgGroup',{name:'fp' ,imgNodes : imgNodes});
        	}else{
        		this._preFetchImg(0);
        	}
        },
        
        _imgPreFetched : function(e){
        	//如果和当前的sid一致
        	if(e._usage != 'fp')return;
        	
        	this.show();
        	this.preFetchInProgress = false;
        	this.waitingForPreFetch--;
        	
    		var imgs = Y.Array.filter(e._imgs.split(','),function(item){
				return item;}),
				start = parseInt(e._start),
				saim = this.articleImgNode,
				fetchedIndex = start + imgs.length;
    		
    		if(start === 0){
   				this.set('imgGroup',{name:e._sid ,imgNodes : imgs});
    		}else{
    			this.__setImgNodes(true, imgs);//添加图片，但不引起set事件。
    		}
    		if(fetchedIndex < (saim.imgNodes.length - 1) && this.waitingForPreFetch > 0){//如果fetchedIndex小于长度减1，继续抓
    			this._preFetchImg(fetchedIndex,this.get('preFetchNum'));
    		}
        	
        	if(saim)saim.fetchedIndex = parseInt(fetchedIndex);
        },
        
        _preFetchImg : function(start){
        	this.preFetchInProgress = true;
        	var imgs = (this.articleImgNode && this.articleImgNode.imgNodes) || [],
        		num = this.get('preFetchNum'),
        		tpl = '/imgprefetch?_start={start}&_num={num}&_imgs={imgs}&_usage=fp',
        		val = {start : start,
        				num : num
        		};
        	var aids = '';
        	for(var i=start;i<start + num;i++){
        		if(i<imgs.length){
        			aids += imgs[i] + ',';
        		}
        	}
        	val.imgs = encodeURIComponent(aids);
        	var url = Y.Lang.sub(tpl,val);
        	this.imgFetchIframe.set('src',url);
        },
        
        __setImgNodes : function(append,imgStrs){
        	var contentBox = this.get('contentBox'),
        	boundingBox = this.get('boundingBox'),
        	imgGroup = this.get('imgGroup'),
        	imgNodes = imgGroup.imgNodes,
        	vh = contentBox.get('viewportRegion'),
        	zIndex = this.get('zIndex'),
        	inComing = this.get('inComingIndex');
        	if(append){
        		var l = imgNodes.length;
    	    	Y.each(imgStrs,function(imgStr){
    	    		var imgNode = Y.Node.create(Lang.sub(FrontpageSlideShow.IMG_TEMPLATE,{url:imgStr}));
    	    		imgNode.setStyles({position:'absolute',top:'0px',opacity:0/*,height:vh.height,width:vh.width*/});
    	    		imgNodes[imgNodes.length] = imgNode;
    	    		this.imgBox.appendChild(imgNode);
    	    	},this);
        	}else{
        		this.imgBox.empty();
    	    	Y.each(imgNodes,function(imgNode){
    	    		imgNode.setStyles({position:'absolute',top:'0px',opacity:0/*,height:vh.height,width:vh.width*/});
    	    		this.imgBox.appendChild(imgNode);
    	    	},this);
        	}
        	
        	this._resetNumberBox();
        	
        	var ilz = imgNodes.length + zIndex; 
        	Y.each(imgNodes,function(n,i){
        		n.setStyle('zIndex',ilz - i);
        	});
        	
        	this.numberBoxNode.setStyle('zIndex',ilz + 2);
        	this.titleBox.setStyle('zIndex',ilz + 2);
        	if(inComing > -1){
        		imgNodes[inComing].setStyle('zIndex',ilz + 1);
        	}
        },
        
        _resetNumberBox : function(){
        	var h = this.get('numBoxAlign') == 'horizontal',vorh;
        	if(h){
        		vorh = 'numbox-align-horizontal';
        	}else{
        		vorh = 'numbox-align-vertical';
        	}
        	var imgNodes = this.get('imgGroup').imgNodes,
        		itemStrs = Y.Array.reduce(imgNodes,'',function(prev,n,i){
        			return Y.Lang.sub(FrontpageSlideShow.MUBMER_ITEM_TEMPLATE,{itemNum:i,vorh:vorh}) + prev;
        		});
        	this.numberBoxNode.setContent(itemStrs);
        },
        _next : function(){
    		var ici = this.get('inComingIndex'),
    			autoSlide = this.get('autoSlide'),
    			imgGroup = this.get('imgGroup'), 
    			num = imgGroup.imgNodes.length;
    		
        	if(this.articleImgNode){
        		num = this.articleImgNode.imgNodes.length;
        	}
        	
    		if(!autoSlide && ici == num - 1)return;
    		if(ici + 1 == num){
				ici = 0;
			}else{
				ici++;
			}
			this.set('inComingIndex', ici);
        },
        
        _previous : function(){
    		var ici = this.get('inComingIndex');
			if(ici  <= 0){
				return;
			}else{
				ici--;
			}
			this.set('inComingIndex', ici);
        },
        
        _autoSlide : function(){
        	if(this.timer){
        		this.timer.cancel();
        	}
        	this.timer = Y.later(this.get('interval')*1000,this,this._next,[],true);
        },
        
        __slide : function(){
         	var inComingIndex = this.get('inComingIndex'),
        		outGoingIndex = this.get('outGoingIndex'),
        		imgGroup = this.get('imgGroup'),
        		imgNodes = imgGroup.imgNodes,
        		titleLength = this.get('titleLength') ? this.get('titleLength') : 10,
        		num = imgNodes.length,
        		visualEffect = this.get('visualEffect');
         	
        	if(this.articleImgNode){
        		num = this.articleImgNode.imgNodes.length;
        	}

//         	Y.log('incoming:' + inComingIndex);
//         	Y.log('outgoing:' + outGoingIndex);
//         	Y.log('num:' + num);
        	if(inComingIndex < 0)return;
        	if(num < 1)return;
        	if(inComingIndex >= num)return;

    		if(imgNodes[outGoingIndex]){
    			imgNodes[outGoingIndex].transition(visualEffect.outgoing);
    			var outz = parseInt(imgNodes[outGoingIndex].getStyle('zIndex'));
    			imgNodes[outGoingIndex].setStyle('zIndex',outz - 1);
    		}
			imgNodes[inComingIndex].transition(visualEffect.incoming);
			imgNodes[inComingIndex].setStyle('zIndex',imgNodes.length);
			this.set('outGoingIndex',inComingIndex);
			var a = this.articleImgNode.articles[inComingIndex],
				o = {title:a.gett('title'),stitle:a.getTitle(titleLength,'...'),url:a.gett('url')};
//			Y.log(a.get('article'));
			this.titleSpan.setContent(Y.Lang.sub(FrontpageSlideShow.TITLE_LINK_TEMPLATE,o));
			
			if(inComingIndex == (imgNodes.length - 1) && this.articleImgNode.imgNodes.length > imgNodes.length){
				if(this.preFetchInProgress){
					this.waitingForPreFetch++;
				}else{
					this._preFetchImg(imgNodes.length,this.get('preFetchNum'));
				}
			}//这里就开始自动抓取了。
        },
        
        _setActiveNumBox : function(){
        	var i = this.get('inComingIndex'),
        		nba = this.numberBoxNode.all('.m3958-innerbox'); 
        	if(i == -1)return;
        	nba.removeClass('slide-active-item');
        	nba.some(function(n){
        		var a = n.one('a');
        		if(a.get('href').split('#')[1] == (i + "")){
        			n.addClass('slide-active-item');
        			return true;
        		}
        	});
        },

        bindUI : function() {
            /*
             * bindUI is intended to be used by the Widget subclass 
             * to bind any event listeners which will drive the Widget UI.
             * 
             * It will generally bind event listeners for attribute change
             * events, to update the state of the rendered UI in response 
             * to attribute value changes, and also attach any DOM events,
             * to activate the UI.
             */
        	
        	this.numberBoxNode.delegate('click',function(e){
        		e.preventDefault();
        		this.set('autoSlide',false);
        		var t = e.currentTarget,
        			a = t.one('a'),
        			i = parseInt(a.get('href').split('#')[1]);
        		this.set('inComingIndex',i);
        		
        	},'div.m3958-innerbox',this);
        	
        	this.numberBoxNode.delegate('click',function(e){
        		e.preventDefault();
        	},'div a',this);
        	
        	var sc = this.get('slideControl');
        	if(sc){
        		sc.on('slideMove',this._moveSlide,this);
        	}
        	Y.Global.on('imgPreFetched',Y.bind(this._imgPreFetched,this));
        	this.after("inComingIndexChange", this._afterInComingIndexChange);
        	this.after("imgGroupChange", this._afterImgGroupChange);
        	this.after("autoSlideChange", this._afterAutoSlideChange);
        	this._next();
        },
        
		fetchArticles : function(start,size,extra){
			var outThis = this,
				myDataSource = this.myDataSource,
				rqst = "&siteId=" + this.get('siteId') + "&_endRow=" + (start + size) + "&_startRow=" + start + "&tags=" + this.get('tags');
		    myDataSource.sendRequest({
		        request: rqst,
		        callback: {
		            success: Y.bind(outThis._datasourceSuccess,outThis,extra),
		            failure: outThis._datasourceFailure
		        }
		    });
		},
		
		_datasourceSuccess : function(extra,e){
			var data = e.response.results;
			this.meta = e.response.meta;
			var ars = Y.Array.map(data,function(a){
				return new Y.M3958.JavascriptArticle({article:a});
			});
			this.articleArrival(ars);
		},
		
		_datasourceFailure : function(e){
			Y.log(e);
		},
       
        _afterAutoSlideChange : function(e){
        	var autoSlide = this.get('autoSlide');
        	if(autoSlide){
        		this._autoSlide();
        	}else{
        		if(this.timer){
        			this.timer.cancel();
        			this.timer = null;
        		}
        	}
        	this.syncUI();
        },
        
        _afterImgGroupChange :function(e){
        	this.__setImgNodes();
        	var ici = this.get('inComingIndex'); 
        	if( ici == -1){
        		this._next();
        	}else if(ici == 0){
        		this.set('inComingIndex',-1);
        		this._next();
        	}else{
        		this.set('inComingIndex',0);
        	}
        },
        
        _getImgUrl : function(asset){
        	return '/asset/' + asset.id + '.' + this.get('imgSize') + asset.extension; 
        },
        
        _afterInComingIndexChange : function(e){
        	this.__slide();
        	this.syncUI();
        },
        
        _moveSlide : function(e){
        	if(e.action == 'next'){
        		this._next();
        	}else if(e.action == 'prev'){
        		this._previous();
        	}
        	this.set('autoSlide',false);
        },

        syncUI : function() {
            /*
             * syncUI is intended to be used by the Widget subclass to
             * update the UI to reflect the initial state of the widget,
             * after renderUI. From there, the event listeners we bound above
             * will take over.
             */
        	
        	var inComingIndex = this.get('inComingIndex'),
        		num = 0,
        		sc = this.get('slideControl');
        	if(this.articleImgNode){
        		num = this.articleImgNode.imgNodes.length;
        	}
        	if(inComingIndex < 0)inComingIndex = 0;
        	var rightNum = num - inComingIndex - 1;
        	if(sc){
        		sc.changeUI({leftNum:inComingIndex,rightNum:rightNum});
        	}
        	this._setActiveNumBox();
    		var autoSlide = this.get('autoSlide');
        }
        // Beyond this point is the FrontpageSlideShow specific application and rendering logic
    });

    Y.namespace("M3958").FrontpageSlideShow = FrontpageSlideShow;

 }, "0.0.1", {requires:["widget", "substitute",'array-extras','javascript-article',"datasource-get", "datasource-jsonschema",'node',"datasource-cache","transition"]});

 
